﻿using CleanArchitecture.Core.Handlers;
using CleanArchitecture.Core.Interfaces.Cache;
using CleanArchitecture.Infrastructure.Entities;
using CleanArchitecture.Infrastructure.Entities.Settings;
using CleanArchitecture.Infrastructure.Entities.SystemTasks;
using CleanArchitecture.Infrastructure.Interfaces.SystemLog;
using CleanArchitecture.Infrastructure.Interfaces.SystemTasks;
using CleanArchitecture.Infrastructure.StartupConfig;
using System;
using System.Linq;

namespace CleanArchitecture.Infrastructure.Services.SystemTasks
{
    /// <summary>
    /// Task
    /// </summary>
    public partial class ProcessTask
    {
        #region Fields

        //private bool? _enabled;

        #endregion

        #region Ctor

        /// <summary>
        /// Ctor for Task
        /// </summary>
        /// <param name="task">Task </param>
        public ProcessTask(SystemTask taskentity)
        {
            ScheduleTask = taskentity;
        }

        #endregion

        #region Utilities

        /// <summary>
        /// Initialize and execute task
        /// </summary>
        private void ExecuteTask()
        {
            try
            {
                var scheduleTaskService = EngineContext.Current.Resolve<ISystemTaskService>();

                //if (!Enabled)
                //    return;

                var type = Type.GetType(ScheduleTask.Type) ??
                    //ensure that it works fine when only the type name is specified (do not require fully qualified names)
                    AppDomain.CurrentDomain.GetAssemblies()
                    .Select(a => a.GetType(ScheduleTask.Type))
                    .FirstOrDefault(t => t != null);
                if (type == null)
                    throw new Exception($"Schedule task ({ScheduleTask.Type}) cannot by instantiated");

                object instance = null;
                instance = EngineContext.Current.Resolve(type);
                if (instance == null)
                {
                    //not resolved
                    instance = EngineContext.Current.ResolveUnregistered(type);
                }

                var task = instance as IScheduleTask;
                if (task == null)
                    return;

                ScheduleTask.LastStartTimeUtc = DateTime.UtcNow;
                //update appropriate datetime properties
                scheduleTaskService.UpdateTask(ScheduleTask);
                bool isOk = task.Execute();
                if (isOk)
                {
                    ScheduleTask.LastStopTimeUtc = ScheduleTask.LastSuccessTimeUtc = DateTime.UtcNow;
                    ScheduleTask.Status = EntityEnumConfig.ScheduleJobActionStatus.Complete;
                }
                else
                {
                    ScheduleTask.LastStopTimeUtc = DateTime.UtcNow;
                    ScheduleTask.Status = EntityEnumConfig.ScheduleJobActionStatus.Error;
                }
                //update appropriate datetime properties
                scheduleTaskService.UpdateTask(ScheduleTask);
            }
            catch(Exception ex)
            {
                //try resolve
                Console.WriteLine(ex.Message);
            }

        }

        /// <summary>
        /// Is task already running?
        /// </summary>
        /// <param name="scheduleTask">Schedule task</param>
        /// <returns>Result</returns>
        //protected virtual bool IsTaskAlreadyRunning(SystemTask scheduleTask)
        //{
        //    //task run for the first time
        //    if (!scheduleTask.LastStartTimeUtc.HasValue && !scheduleTask.LastStopTimeUtc.HasValue)
        //        return false;

        //    var lastStartUtc = scheduleTask.LastStartTimeUtc ?? DateTime.UtcNow;

        //    //task already finished
        //    if (scheduleTask.LastStopTimeUtc.HasValue && lastStartUtc < scheduleTask.LastStopTimeUtc)
        //        return false;

        //    //task wasn't finished last time
        //    if (lastStartUtc.AddSeconds(scheduleTask.Seconds) <= DateTime.UtcNow)
        //        return false;

        //    return true;
        //}

        #endregion

        #region Methods

        /// <summary>
        /// Executes the task
        /// </summary>
        /// <param name="throwException">A value indicating whether exception should be thrown if some error happens</param>
        /// <param name="ensureRunOncePerPeriod">A value indicating whether we should ensure this task is run once per run period</param>
        public void Execute(bool throwException = false/*, bool ensureRunOncePerPeriod = true*/)
        {
            if (ScheduleTask == null)// || !Enabled)
                return;

            //if (ensureRunOncePerPeriod)
            //{
            //    //task already running
            //    if (IsTaskAlreadyRunning(ScheduleTask))
            //        return;

            //    //validation (so nobody else can invoke this method when he wants)
            //    if (ScheduleTask.LastStartTimeUtc.HasValue && (DateTime.UtcNow - ScheduleTask.LastStartTimeUtc).Value.TotalSeconds < ScheduleTask.Seconds)
            //        //too early
            //        return;
            //}

            try
            {
                //get expiration time
                //var expirationInSeconds = Math.Min(ScheduleTask.Seconds, 300) - 1;
                var expiration = TimeSpan.FromSeconds(300);

                //execute task with lock
                var locker = EngineContext.Current.Resolve<ILocker>();
                locker.PerformActionWithLock(ScheduleTask.Type, expiration, ExecuteTask);
            }
            catch (Exception exc)
            {
                var scheduleTaskService = EngineContext.Current.Resolve<ISystemTaskService>();
                var commonSettings = EngineContext.Current.Resolve<CommonSettings>();
                var scheduleTaskUrl = $"{commonSettings.ServerUrl}{DefaultRoutePath.ScheduleTaskPath}";

                //ScheduleTask.Enabled = (ScheduleTask.StopOnError == EntityEnumConfig.YesorNo.YES)
                //                     ? EntityEnumConfig.YesorNo.No : EntityEnumConfig.YesorNo.YES ;
                ScheduleTask.Status = EntityEnumConfig.ScheduleJobActionStatus.Error;
                ScheduleTask.LastStopTimeUtc = DateTime.UtcNow;
                scheduleTaskService.UpdateTask(ScheduleTask);

                var message = string.Format(SystemTaskMessage.ScheduleTasksErrorOnExecute, 
                                     ScheduleTask.TaskName, exc.Message, ScheduleTask.Type, scheduleTaskUrl);
                //log error
                var logger = EngineContext.Current.Resolve<ISystemLogService>();
                logger.Error(message);
                if (throwException)
                    throw;
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Schedule task
        /// </summary>
        public SystemTask ScheduleTask { get; }

        /// <summary>
        /// A value indicating whether the task is enabled
        /// </summary>
        //public bool Enabled
        //{
        //    get
        //    {
        //        if (!_enabled.HasValue)
        //            _enabled = (ScheduleTask?.Enabled == EntityEnumConfig.YesorNo.YES)
        //                      ? true : false;

        //        return _enabled.HasValue && _enabled.Value;
        //    }

        //    set => _enabled = value;
        //}

        #endregion
    }
}
